My thoughts about the language
My Thoughts About JavaScript
JavaScript has been a core part of my development journey, and over the years, I've developed a set of opinions—both positive and negative—about the language. Here’s my take on JavaScript: what I like, what I dislike, and some observations from working with it across different projects.
What I Like About JavaScript
1. Flexibility and Expressiveness
JavaScript allows for multiple paradigms—functional, object-oriented, or even procedural. This flexibility makes it easy to adapt based on the problem at hand.
2. Dynamic Nature
I like the ease of working with dynamic objects and serialization. JavaScript’s ability to shape objects on the fly and manipulate data structures without rigid type constraints makes it incredibly productive, especially for frontend work.
3. Ecosystem and Community
While I have mixed feelings about the ecosystem (more on that later), JavaScript’s community is active, and new solutions keep emerging.
4. Event-Driven Model
The event loop and async programming model make JavaScript well-suited for handling user interactions and network requests in a non-blocking way.
5. Destructuring and Spread Operators
JavaScript has added some powerful syntactic sugar over the years. Destructuring and spread/rest operators make working with objects and arrays much more convenient.
6. NPM's Structure
I like how npm is structured—having node_modules
co-located with the source code just makes sense. It’s not the 90s; we have plenty of storage space, and this approach makes dependency management simple. Plus, it's easy to .gitignore
.
What I Dislike About JavaScript
1. Inconsistent Standard Library
Some APIs feel like they were designed in isolation. For example, Array.prototype.includes()
exists, but Object.prototype.includes()
does not.
2. Type Coercion Oddities
Loose typing is useful but can lead to some surprising results. The quirks around ==
and how JavaScript coerces values can sometimes lead to debugging headaches.
3. Mutation by Default
Many built-in methods mutate the original object or array, leading to unintended side effects if you're not careful.
4. Tooling Overload & Library Madness
There are way too many libraries for tiny things that shouldn’t need a library in the first place. The whole ESM vs. CommonJS situation is a nightmare—no matter how experienced you are, you still run into issues. The import/export system just doesn’t feel as smooth as other language ecosystems. If tree-shaking works, then fine, but needing a library for trivial things feels unnecessary.
5. Too Many Ways to Loop
JavaScript offers an overwhelming number of ways to iterate: map
, filter
, forEach
, traditional for
loops, for...of
, for...in
—and each has its own quirks. I often find myself debating which one to use rather than focusing on the actual problem. Sometimes, I write a concise ES6 filter
one-liner, only for a format-on-save to expand it back to the length of a traditional for
loop.
6. Callback Hell (Even With Promises)
While async/await improved things, legacy codebases still suffer from deeply nested callbacks and promise chains that can be difficult to manage.
7. No True Pointers (and Reference vs. Value Confusion)
Both JavaScript and Python lack explicit pointers, which can sometimes make dealing with references more confusing than it needs to be. The common mental shortcut—"primitives are passed by value, objects are passed by reference"—isn't always enough.
- Mutation confusion: Passing an object to a function lets you modify it in place, but reassigning it inside the function doesn’t update the original reference.
- Unexpected behavior with copying: Shallow vs. deep copies add another layer of complexity. While you can deep copy using
structuredClone()
,JSON.parse(JSON.stringify(obj))
, or third-party utilities like Lodash’scloneDeep()
, this all feels unnecessary for what should be a simple concept. - Lack of true memory control: Unlike languages with pointers, you can't directly manage memory, and references are more abstract, making certain optimizations harder.
Pointers —when kept simple, like in Go (which avoids pointer arithmetic)—would be more intuitive. Having a syntactical way to indicate whether I want a reference or a copy, without relying on implicit behavior, would reduce a lot of the cognitive overhead.
Observations
- JavaScript Is the Default Language of the Web: Despite its quirks, JavaScript is the default language of the web, and that’s unlikely to change soon. Not only that, but I wouldn’t want it to change. Reactive frameworks (and stores) were a huge step forward in managing local application state and performing UI updates. th3o.gg has a great video on this, breaking down codebase functionality vs. codebase complexity:
-
JavaScript Adapts Quickly: The language evolves fast, and features like optional chaining and top-level await show that JavaScript is constantly improving.
-
Python Feels Simpler for DS&A: In Python, using a dictionary (hashmap) gives you constant lookup time, and even if you need an array, you can just store the index as the key. I haven’t done DS&A in Python in a while, but I feel like it lets you focus on solving the problem rather than getting bogged down by the constraints of your chosen iteration method.
Overall, while JavaScript has its annoyances, it remains a powerful and indispensable tool for modern development. Understanding its quirks is key to making the most of it.